/*
 * C part of the pmode files
 */


#include <i386_arch.h>
#include <i386_pmode.h>
#include <string.h> /* memmove() */
#include <interrupts.h> /* service routines */
#include <globals.h>  /* RELOC_PATCH */


/* global variables, internal to this file */
GDTR_T gdtr;
IDTR_T idtr;

GDT_Entry_T GDT[GDT_ENTRIES];
GDT_Entry_T IDT[IDT_ENTRIES];

TSS_T TSS;

// pointer to TSS.esp0
// used by task switching assembler routine (isr_all_ints)
dword_t tss_esp0;

void setup_IDT_Entry(IDT_Entry_T *entry, byte_t type, word_t selector, dword_t isr)
  {
    entry->offset_0_15=isr & 0xFFFF;
    entry->selector=selector;
    entry->not_used=0;
    entry->access=type;
    entry->offset_16_31=(isr>>16) & 0xFFFF;
  }

void setup_GDT_Entry(GDT_Entry_T *entry, byte_t gran, byte_t access, dword_t base, word_t limit)
  {
    entry->limit_0_15=limit;
    entry->base_0_15=base & 0xFFFF;
    entry->base_16_23=(base>>16) & 0xFF;
    entry->access=access;
    entry->gran=gran;
    entry->base_24_31=(base>>24) & 0xFF;
  }  

void place_IDT_Entry(int num,IDT_Entry_T *desc)
  {    
    //memmove((void *)IDT_BASE+DESC_SIZE*num,desc,DESC_SIZE);
    memmove((void *)&IDT[num],desc,DESC_SIZE);
  }

void place_GDT_Entry(int num,GDT_Entry_T *desc)
  {    
    //memmove((void *)GDT_BASE+DESC_SIZE*num,desc,DESC_SIZE);
    memmove((void *)&GDT[num],desc,DESC_SIZE);
  }

void enable_interrupts(void)
  {
    //set_if();    
    asm("sti");
  }

void disable_interrupts(void)
  {
    //clear_if();
    asm("cli");
  }

void prepare_IDT(void)
  {
    IDT_Entry_T desc;
    int i;

    //Make a descriptor pointing to the isr_unhandled() interrupt service routine
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_unhandled);

    //Fill IDT with the default (unhandled interrupt) entry
    for (i=0;i<IDT_ENTRIES;i++)
      place_IDT_Entry(i,&desc);


    // New method:
    //setup_IDT_Entry(&IDT[DIVIDE_VECTOR],IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_divide);

    /*
     *  Insert Intel defined interrputs
     */
    // [00] - Divide Error
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_divide);
    place_IDT_Entry(DIVIDE_VECTOR,&desc);
    // [01] - Debug
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_debug);
    place_IDT_Entry(DEBUG_VECTOR,&desc);
    // [02] - NMI
    setup_IDT_Entry(&desc,IDT_TYPE_INTR,GDT_DESC_CODE,(dword_t)&isr_nmi);
    place_IDT_Entry(NMI_VECTOR,&desc);
    // [03] - Breakpoint
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_breakpoint);
    place_IDT_Entry(BREAKPOINT_VECTOR,&desc);
    // [04] - Overflow
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_overflow);
    place_IDT_Entry(OVERFLOW_VECTOR,&desc);
    // [05] - Bound Range Exceeded 
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_bound);
    place_IDT_Entry(BOUND_VECTOR,&desc);
    // [06] - Invalid Opcode [Pentium Pro+]
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_opcode);
    place_IDT_Entry(OPCODE_VECTOR,&desc);
    // [07] - No PFU
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_nofpu);
    place_IDT_Entry(NOFPU_VECTOR,&desc);
    // [08] - Double Fault
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_doublefault);
    place_IDT_Entry(DBLFAULT_VECTOR,&desc);
    // [09] - Coprocessor Segment Overrun [386 only] (now is reserved by Intel)
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_fpuoverrun);
    place_IDT_Entry(FPUOVERRUN_VECTOR,&desc);
    // [10] - Invalid TSS
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_invalidtss);
    place_IDT_Entry(INVALIDTSS_VECTOR,&desc);
    // [11] - Segment Not Present
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_nosegment);
    place_IDT_Entry(SEGMENT_VECTOR,&desc);
    // [12] - Stack-Segment fault
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_stacksegment);
    place_IDT_Entry(STACK_VECTOR,&desc);
    // [13] - General Protection Fault (GPF)
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_gpf);
    place_IDT_Entry(GPF_VECTOR,&desc);
    // [14] - Page Fault
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_pagefault);
    place_IDT_Entry(PAGE_VECTOR,&desc);
    // [15] - (Intel reserved)
    //setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_reserved);
    //place_IDT_Entry(INT0x0F_VECTOR,&desc);
    // [16] - Math Fault
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_mathfault);
    place_IDT_Entry(FPU_VECTOR,&desc);
    // [17] - Alignment Check [486+]
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_alignment);
    place_IDT_Entry(ALIGN_VECTOR,&desc);
    // [18] - Machine Check [Pentium+]
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_machine);
    place_IDT_Entry(MACHINE_VECTOR,&desc);
    // [19] - FPU Exception [Pentium III+]
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_fpesimd);
    place_IDT_Entry(FPUPIII_VECTOR,&desc);
    // [20-31] - (Intel reserved) [As for December 2002]
    //setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_reserved);
    //for (i=INT0x14_VECTOR;i<=INT0x1F_VECTOR;i++)
    //  place_IDT_Entry(i,&desc);


    /*
     * Place IRQs Service Routines
     */

    // Initialize data structures to manage IRQs
    initialize_IRQ();

    // IRQ 00:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_00);
    place_IDT_Entry(IRQ00_VECTOR,&desc);
    // IRQ 01:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_01);
    place_IDT_Entry(IRQ01_VECTOR,&desc);
    // IRQ 02:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_02);
    place_IDT_Entry(IRQ02_VECTOR,&desc);
    // IRQ 03:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_03);
    place_IDT_Entry(IRQ03_VECTOR,&desc);
    // IRQ 04:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_04);
    place_IDT_Entry(IRQ04_VECTOR,&desc);
    // IRQ 05:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_05);
    place_IDT_Entry(IRQ05_VECTOR,&desc);
    // IRQ 06:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_06);
    place_IDT_Entry(IRQ06_VECTOR,&desc);
    // IRQ 07:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_07);
    place_IDT_Entry(IRQ07_VECTOR,&desc);
    // IRQ 08:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_08);
    place_IDT_Entry(IRQ08_VECTOR,&desc);
    // IRQ 09:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_09);
    place_IDT_Entry(IRQ09_VECTOR,&desc);
    // IRQ 10:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_10);
    place_IDT_Entry(IRQ10_VECTOR,&desc);
    // IRQ 11:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_11);
    place_IDT_Entry(IRQ11_VECTOR,&desc);
    // IRQ 12:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_12);
    place_IDT_Entry(IRQ12_VECTOR,&desc);
    // IRQ 13:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_13);
    place_IDT_Entry(IRQ13_VECTOR,&desc);
    // IRQ 14:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_14);
    place_IDT_Entry(IRQ14_VECTOR,&desc);
    // IRQ 15:
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_irq_15);
    place_IDT_Entry(IRQ15_VECTOR,&desc);

    /*
     * Place syscall interrupt
     */
    setup_IDT_Entry(&desc,IDT_TYPE_TRAP,GDT_DESC_CODE,(dword_t)&isr_syscall);
    place_IDT_Entry(SYSCALL_VECTOR,&desc);


    // Prepare the IDTR (IDT Register)
    //idtr.base=((dword_t)(&IDT))-RELOC_PATCH;
    //idtr.limit=(word_t)DESC_SIZE*IDT_ENTRIES-1;

    idtr.base=((dword_t)(&IDT));
    idtr.limit=(word_t)DESC_SIZE*IDT_ENTRIES-1;


    // Load the IDTR
    //load_idt((IDTR_T *)(((dword_t)&idtr)-RELOC_PATCH));
    load_idt((IDTR_T *)(((dword_t)&idtr)));
  }

void prepare_GDT(void)
  {
    // Make a new GDT

    // 0 -- NULL DESCRIPTOR (0x00)
    setup_GDT_Entry(&GDT[0],0,0,0,0);

    // 1 -- CODE DESCRIPTOR (0x08)
    setup_GDT_Entry(&GDT[1],0xCF,0x9A,0,0xFFFF);

    // 2 -- DATA DESCRIPTOR (0x10)
    setup_GDT_Entry(&GDT[2],0xCF,0x92,0,0xFFFF);

    // 3 -- USER CODE DESCRIPTOR (0x18)
    // instead of 0xCF use 0xC7 to limit at 2GB
    setup_GDT_Entry(&GDT[3],0xCF,0xFA,0,0xFFFF);

    // 4 -- USER DATA DESCRIPTOR (0x20)
    // instead of 0xCF use 0xC7 to limit at 2GB
    setup_GDT_Entry(&GDT[4],0xCF,0xF2,0,0xFFFF);

    // 5 -- TSS  DESCRIPTOR (0x28)
    setup_GDT_Entry(&GDT[5],0x00,0x89,&TSS,sizeof(TSS_T));

    // Prepare the GDTR (GDT Register)
    //gdtr.base=((dword_t)(&GDT))-RELOC_PATCH;
    //gdtr.limit=(word_t)DESC_SIZE*GDT_ENTRIES-1;

    gdtr.base=((dword_t)(&GDT));
    gdtr.limit=(word_t)DESC_SIZE*GDT_ENTRIES-1;


    // Load the GDTR
    //load_gdt((GDTR_T *)(((dword_t)&gdtr)-RELOC_PATCH));
    load_gdt((GDTR_T *)(((dword_t)&gdtr)));
  }

void prepare_TSS(void)
  {
    // Prepare pointer to TSS.esp0
    tss_esp0 = (dword_t) &TSS.esp0;
    
    /* Initialize TSS */
  
    TSS.backlink = 0;
    TSS.esp0 = 0;
    TSS.ss0 = GDT_DESC_DATA;
    TSS.esp1 = 0;
    TSS.ss1 = 0;
    TSS.esp2 = 0;
    TSS.ss2 = 0;
    TSS.cr3 = 0;
    TSS.eip = 0;
    TSS.eflags = 0x0202;
    TSS.eax = 0x0BAD;
    TSS.ecx = 0x0BAD;
    TSS.edx = 0x0BAD;
    TSS.ebx = 0x0BAD;
    TSS.esp = 0;
    TSS.ebp = 0;
    TSS.esi = 0;
    TSS.edi = 0;
    TSS.es = 0x10;
    TSS.cs = 0x08;
    TSS.ss = 0x10;
    TSS.ds = 0x10;
    TSS.fs = 0x10;
    TSS.gs = 0x10;
    TSS.ldtr = 0;
    TSS.trace = 0;
    TSS.iomap_base = 0;  

    // Load Task register
    load_tr(GDT_DESC_TSS);
}
